#ifndef __INCtemplateh
#define __INCtemplateh
#include <string>
#include <cstdio>
#include <limits>
#include <utils.h>
#include <type.h>

namespace utils
{
    template <typename... Args>
    inline std::string format(const char* format, Args&&... args)
    {
        if (!format) 
        {
            return "";
        }

        const int size = std::snprintf(nullptr, 0, format, std::forward<Args>(args)...) + 1;
        if (size <= 1) 
        {
            return "";
        }

        std::string result(static_cast<size_t>(size), '\0');
        const int written = std::snprintf(&result[0], static_cast<size_t>(size),
                                        format, std::forward<Args>(args)...);

        return written < 0 ? "" : (result.resize(static_cast<size_t>(written)), result);
    }

    template <class T>
    inline std::string to_hexstring(T _Val)
    {
        return format("0x%lx", _Val);
    }
    
    template <typename T>
    typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value, T>::type
    to_Signed(const char * str_val, T default_val)
    {
        if (!str_val || (*str_val == EOFS))
        {
            return default_val;
        }

        char *endptr;
        errno = 0;
        long long val;

        if constexpr (sizeof(T) <= sizeof(long))
        {
            val = std::strtol(str_val, &endptr, 0);
        }
        else
        {
            val = std::strtoll(str_val, &endptr, 0);
        }

        if (*endptr != EOFS || errno == ERANGE ||
            val < static_cast<long long>(std::numeric_limits<T>::min()) ||
            val > static_cast<long long>(std::numeric_limits<T>::max()))
        {
            return default_val;
        }

        return static_cast<T>(val);
    }

    template <typename T>
    typename std::enable_if<std::is_integral<T>::value && !std::is_signed<T>::value, T>::type
    to_Unsigned(const char * str_val, T default_val)
    {
        if (!str_val || (*str_val == EOFS))
        {
            return default_val;
        }

        if (*str_val == '-')
        {
            return default_val;
        }

        char *endptr;
        errno = 0;
        unsigned long long val;

        if constexpr (sizeof(T) <= sizeof(unsigned long))
        {
            val = std::strtoul(str_val, &endptr, 0);
        }
        else
        {
            val = std::strtoull(str_val, &endptr, 0);
        }

        if (*endptr != EOFS || errno == ERANGE ||
            val > static_cast<unsigned long long>(std::numeric_limits<T>::max()))
        {
            return default_val;
        }

        return static_cast<T>(val);
    }

    template <typename T>
    typename std::enable_if<std::is_floating_point<T>::value, T>::type
    to_Floating(const char * str_val, T default_val)
    {
        if (!str_val || (*str_val == EOFS))
        {
            return default_val;
        }

        char *endptr;
        errno = 0;
        long double val;

        if constexpr (std::is_same_v<T, long double>)
        {
            val = std::strtold(str_val, &endptr);
        }
        else if constexpr (std::is_same_v<T, double>)
        {
            val = std::strtod(str_val, &endptr);
        }
        else
        { // float
            val = std::strtof(str_val, &endptr);
        }

        if (*endptr != EOFS || errno == ERANGE)
        {
            return default_val;
        }

        return static_cast<T>(val);
    }
};
#endif